package com.gitee.wsl.serialization.json

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.long

/** Put longs in strings if they can't be represented exactly as doubles. */
internal object LongSerializer : KSerializer<Long> {
    override val descriptor = PrimitiveSerialDescriptor(
        "LongSerializer",
        PrimitiveKind.LONG,
    )

    override fun deserialize(decoder: Decoder): Long {
        return when (val jsonElement = (decoder as JsonDecoder).decodeJsonElement()) {
            is JsonArray, is JsonObject, JsonNull -> throw SerializationException("expected a Long")
            is JsonPrimitive -> jsonElement.long
        }
    }

    override fun serialize(encoder: Encoder, value: Long) {
        if (value in MIN_SAFE_INTEGER..MAX_SAFE_INTEGER) {
            encoder.encodeLong(value)
        } else {
            encoder.encodeString(value.toString())
        }
    }

    // All integers in this range can be expressed as a double. Outside of it they can't.
    private const val MIN_SAFE_INTEGER = -9007199254740991L // -(2^53 – 1)
    private const val MAX_SAFE_INTEGER = 9007199254740991L // 2^53 – 1
}
